import VideoAbstract from './VideoAbstract';

export interface WsPropsType {
	el: HTMLVideoElement; // 绑定元素
	type: 'real' | 'record'; //  视频类型
	/**
	 * 兼容老版本字段 【已废弃】
	 * 在 ws/wss 直连模式下, serverIp 表示MTS流媒体服务 IP
	 * 在 代理 模式下, serverIp 表示的是 代理服务器的IP 地址
	 */
	serverIp: string;
	/**
	 * V1.2.8 版本新增字段
	 * 解码库资源文件前缀
	 */
	prefixUrl: string;
	/**
	 * V1.2.8 版本新增字段
	 * websocket协议，http支持wss协议拉流，因此增加protocol配置
	 */
	protocol: string;
	/**
	 * V1.2.8 版本新增字段[组合使用]
	 * @desc 注意事项： 用于有内外网场景和分布式场景
	 * @param isIntranet 是否内网, 用于有内外网环境，多台大华服务器[分布式]拉流场景配置
	 * @param intranetMap 映射关系，结构 { "内网ip": "外网ip", ... }
	 *
	 */
	isIntranet: boolean;
	intranetMap: Record<string, string>;
	/**
	 * V1.2.7 版本新增字段
	 * 用于只有内网
	 * @desc 代理 模式下，代理服务器的IP 地址
	 * 如果代理服务有端口则拼接端口
	 *
	 */
	proxyServerIp: string;
	/**
	 * V1.2.7 版本新增字段
	 * 用于只有内网
	 * @desc 流媒体服务 IP 地址
	 * 如果拉流的 9100 9320 端口发生修改，则需要后面拼接修改后的端口
	 */
	streamServerIp: string;
	/**
	 * V1.2.8 版本新增字段
	 * 优先级最高，该方法传入后
	 * 结构如下：
	 * eg:
	 * (rtspUrl) => {
	 *     return "wss://{ip}:9102"
	 * }
	 * 配置后：protocol, isIntranet, proxyServerIp, streamServerIp
	 */
	setWSUrl: string;
	/**
	 * 窗口数量
	 */
	num: number;
	/**
	 * 最大窗口数量
	 */
	maxNum: number;
	/**
	 * 是否显示工具栏
	 */
	showControl: boolean;
	/**
	 * V1.2.7 版本新增字段
	 * @desc 模式类型 是直连模式/代理模式
	 */
	useNginxProxy: boolean;
	showIcons: string;
	/**
	 * v1.3.0 新增，是否支持拖拽
	 */
	draggable: string;
	/**
	 * 是否显示回放进度条
	 */
	showRecordProgressBar: boolean;
	downloadMp4Record: string;
	useH265MSE: string;

	receiveMessageFromWSPlayer: () => Promise<any>;

	/**
	 * 获取实时预览rtsp接口 (不要做任何改动, 只传入方法, API.getRealmonitor() 是错误的， 会报错)
	 */
	getRealRtsp: () => Promise<any>;
	/**
	 * 获取对讲rtsp接口  (不要做任何改动, 只传入方法, API.getTalkRtsp() 是错误的， 会报错)
	 */
	getTalkRtsp: () => Promise<any>;
	/**
	 * 关闭对讲rtsp接口  (不要做任何改动, 只传入方法, API.stopTalk() 是错误的， 会报错)
	 */
	stopTalk: () => Promise<any>;
	/**
	 * 获取录像列表接口  (不要做任何改动, 只传入方法, API.getRecords() 是错误的， 会报错)
	 */
	getRecords: () => Promise<any>;
	/**
	 * 根据时间形式获取录像rtsp接口 (不要做任何改动, 只传入方法, API.getRecordRtspByTime() 是错误的， 会报错)
	 */
	getRecordRtspByTime: () => Promise<any>;
	/**
	 * 根据文件形式获取录像rtsp接口 (不要做任何改动, 只传入方法, API.getRecordRtspByFile() 是错误的， 会报错)
	 */
	getRecordRtspByFile: () => Promise<any>;
}

export default class WSPlayerVideo extends VideoAbstract {
	static instance: WSPlayerVideo | null = null;

	private playerType: 'real' | 'record' = 'real';
	private realPlayer: Window['WSPlayer'] | null;
	private recordPlayer: Window['WSPlayer'] | null;

	// 当前通道
	private currentChannelId: string = '';
	// 当前播放下标
	private playIndex: number = 0;
	// 当前窗口数量
	private playNum: number = 0;

	private onLoadedCallback: ((data: any) => void) | null = null;
	private onErrorCallback: ((error: any) => void) | null = null;

	private constructor(props: WsPropsType) {
		super();

		this.playerType = props.type;

		switch (props.type) {
			case 'real':
				this.initRealPlayer(props);
				break;
			case 'record':
				this.initRecordPlayer(props);
				break;
			default:
				throw new Error('wsplayer无此类型');
		}
	}

	initRealPlayer(props: WsPropsType) {
		this.realPlayer = new window.WSPlayer({
			el: props.el,
			type: props.type,
			serverIp: props.serverIp,
			prefixUrl: props.prefixUrl || '../js/player',
			protocol: props.protocol,
			isIntranet: props.isIntranet,
			intranetMap: props.intranetMap,
			proxyServerIp: props.proxyServerIp,
			streamServerIp: props.streamServerIp,
			setWSUrl: props.setWSUrl,
			config: {
				num: props.num,
				maxNum: props.maxNum,
				showControl: props.showControl,
				useNginxProxy: props.useNginxProxy,
				showIcons: props.showIcons || {
					streamChangeSelect: true, // 主辅码流切换
					talkIcon: true, // 对讲功能按钮
					localRecordIcon: true, // 本地录像功能按钮
					audioIcon: true, // 开启关闭声音按钮
					snapshotIcon: true, // 抓图按钮
					closeIcon: true, // 关闭视频按钮
				},
				draggable: props.draggable,
				downloadMp4Record: props.downloadMp4Record,
				useH265MSE: props.useH265MSE,
			},
			receiveMessageFromWSPlayer: this.__receiveMessageFromWSPlayer.bind(this),
			getRealRtsp: props.getRealRtsp,
			getTalkRtsp: props.getTalkRtsp,
			stopTalk: props.stopTalk,
		});
	}

	initRecordPlayer(props: WsPropsType) {
		this.recordPlayer = new window.WSPlayer({
			el: props.el,
			type: props.type,
			serverIp: props.serverIp,
			prefixUrl: props.prefixUrl || '../js/player',
			protocol: props.protocol,
			isIntranet: props.isIntranet,
			intranetMap: props.intranetMap,
			proxyServerIp: props.proxyServerIp,
			streamServerIp: props.streamServerIp,
			setWSUrl: props.setWSUrl,
			config: {
				num: props.num,
				maxNum: props.maxNum,
				showControl: props.showControl,
				useNginxProxy: props.useNginxProxy,
				showIcons: props.showIcons || {
					streamChangeSelect: true, // 主辅码流切换
					talkIcon: true, // 对讲功能按钮
					localRecordIcon: true, // 本地录像功能按钮
					audioIcon: true, // 开启关闭声音按钮
					snapshotIcon: true, // 抓图按钮
					closeIcon: true, // 关闭视频按钮
				},
				showRecordProgressBar: props.showRecordProgressBar,
				draggable: props.draggable,
				downloadMp4Record: props.downloadMp4Record,
				useH265MSE: props.useH265MSE,
			},
			receiveMessageFromWSPlayer: this.__receiveMessageFromWSPlayer.bind(this),
			getRecords: props.getRecords,
			getRecordRtspByTime: props.getRecordRtspByTime,
			getRecordRtspByFile: props.getRecordRtspByFile,
		});
	}

	static getInstance(props: WsPropsType): WSPlayerVideo {
		return WSPlayerVideo.instance || (WSPlayerVideo.instance = new WSPlayerVideo(props));
	}

	bindVideo(): void {
		throw new Error('Method not implemented.');
	}

	unbindVideo(): void {
		throw new Error('Method not implemented.');
	}

	setCurrentTime(time: number): void {
		console.log(time);
		throw new Error('Method not implemented.');
	}

	/**
	 * 播放
	 * @param opt.channelList: {Array<Object>} 必填，通道列表
	 * @param opt.streamType: {Number|String} 选填，码流类型，不填默认播放辅码流1，若不存在辅码流1，则自动切换到主码流  1-主码流 2-辅码流1 3-辅码流2
	 * @param opt.windowIndex: {Number} 选填，指定从哪个窗口开始播放。不填默认从选中的窗口开始播放
	 * @param opt.startTime {String|Number} 开始时间  必选  timestamp到秒
	 * @param opt.endTime {String|Number} 结束时间  必选  timestamp到秒
	 * @param opt.recordSource {String|Number} 录像来源  必选 2表示设备录像  3表示中心录像
	 * @param opt.recordType {String|Number} 录像类型 可选
	 */
	// channelList: [{
	//     id: channelCode, // {String} 通道编码 -- 用于预览，必填
	//     deviceCode: deviceCode, // {String} 设备编码 -- 用于对讲，对讲必填，无对讲功能可不填
	//     deviceType: deviceType, // {String} 设备类型 -- 用于对讲，对讲必填，无对讲功能可不填
	//     channelSeq: channelSeq, // {String|Number} 通道序号 -- 用于对讲，对讲必填，无对讲功能可不填
	//     cameraType: cameraType, // {String|Number} 摄像头类型 -- 用于云台，云台必填，无云台功能可不填
	//     capability: capability, // {String} 能力集 -- 用于云台，云台必填，无云台功能可不填
	// }]
	play(opt: {
		channelList: {
			id: string;
			deviceCode: string;
			deviceType: string;
			channelSeq: string;
			cameraType: string;
			capability: string;
		}[];
		streamType: 1 | 2 | 3;
		windowIndex: number;
		startTime: number;
		endTime: number;
		recordSource: 2 | 3;
		recordType?: string;
	}): void {
		if (this.playerType === 'record') {
			this.playRecordVideo(opt);
		}

		if (this.playerType === 'real') {
			this.playRealVideo(opt);
		}
	}
	/**
	 * 播放录像回放
	 * @param opt.channelList {Array<Object>} 通道集合 必填
	 * @param opt.startTime {String|Number} 开始时间  必选  timestamp到秒
	 * @param opt.endTime {String|Number} 结束时间  必选  timestamp到秒
	 * @param opt.recordSource {String|Number} 录像来源  必选 2表示设备录像  3表示中心录像
	 * @param opt.streamType {String|Number} 码流类型 可选
	 * @param opt.recordType {String|Number} 录像类型 可选
	 */
	playRecordVideo(opt: {
		channelList: {
			id: string;
			deviceCode: string;
			deviceType: string;
			channelSeq: string;
			cameraType: string;
			capability: string;
		}[];
		streamType: 1 | 2 | 3;
		startTime: number;
		endTime: number;
		recordSource: 2 | 3;
		recordType?: string;
	}) {
		this.recordPlayer && this.recordPlayer.playRecordVideo(opt);
	}

	/**
	 * 播放实时预览视频
	 * @param opt.channelList: {Array<Object>} 必填，通道列表
	 * @param opt.streamType: {Number|String} 选填，码流类型，不填默认播放辅码流1，若不存在辅码流1，则自动切换到主码流  1-主码流 2-辅码流1 3-辅码流2
	 * @param opt.windowIndex: {Number} 选填，指定从哪个窗口开始播放。不填默认从选中的窗口开始播放
	 */
	playRealVideo(opt: {
		channelList: {
			id: string;
			deviceCode: string;
			deviceType: string;
			channelSeq: string;
			cameraType: string;
			capability: string;
		}[];
		streamType: 1 | 2 | 3;
		windowIndex: number;
	}) {
		this.realPlayer &&
			this.realPlayer.playRealVideo({
				channelList: opt.channelList,
				streamType: opt.streamType,
				windowIndex: opt.windowIndex,
			});
	}

	pause(index: number): void {
		if (this.playerType === 'record') {
			this.recordPlayer && this.recordPlayer.pause(index);
		}

		if (this.playerType === 'real') {
			throw new Error('实时视频无法暂停');
		}
	}
	destroy(): void {
		if (this.playerType === 'real') {
			this.realPlayer.destroy();
		}
		if (this.playerType === 'record') {
			this.recordPlayer.destroy();
		}
	}

	__receiveMessageFromWSPlayer(method: string, data: any) {
		switch (method) {
			// ------------- 公共事件 ---------------------
			case 'initializationCompleted':
				// 初始化完成，可调用播放方法（适用于动态加载解码库）
				break;
			case 'realSuccess': // 实时预览成功
				console.log('实时预览成功');
				break;
			case 'realError': // 实时预览失败
				console.log('实时预览失败', data);
				break;
			case 'talkError': // 对讲失败
				console.log('对讲失败');
				break;
			case 'recordSuccess': // 录像回放成功
				console.log('录像回放成功');
				break;
			case 'recordError': // 录像回放失败
				console.log('录像回放失败', data);
				break;
			case 'recordFinish':
				console.log('当前录像播放完成', data);
				break;
			case 'selectWindowChanged': // 选中的窗口发生改变
				this.currentChannelId = data.channelId;
				this.playIndex = data.playIndex;
				break;
			case 'windowNumChanged': // 播放器显示的路数发生改变
				this.playNum = data;
				break;
			case 'closeVideo': // 视频关闭
				// 点击关闭按钮引发的视频关闭进行提示
				// 切换视频引发的视频关闭不进行提示
				if (!data.changeVideoFlag) {
					console.log(`窗口${data.selectIndex}的视频已关闭`);
				}
				break;
			case 'statusChanged': // 视频状态发生改变
				break;
			case 'getVideoFrameInfo':
				// 获取视频帧信息
				break;
			case 'getAudioFrameInfo':
				// 获取视频内音频帧信息
				break;
			case 'getPlayBackStreamRange':
				// 获取录像回放时长
				console.log('getPlayBackStreamRange', data);
				break;
			case 'dragWindow':
				// 拖拽窗口
				console.log(`窗口${data.dragIndex}的视频拖拽到窗口${data.dropIndex}上`);
				break;
			case 'errorInfo': // 错误信息提示
				console.log(data, '可打印查看错误消息');
				break;
		}
	}
	onLoaded(callback?: ((data: any) => void) | undefined): void {
		if (typeof callback === 'function') this.onLoadedCallback = callback;
	}
	onError(callback?: ((error: any) => void) | undefined): void {
		if (typeof callback === 'function') this.onErrorCallback = callback;
	}
}
